package main

import (
	"fmt"
	"learn-go/6.interface/retriever/mock"
	"learn-go/6.interface/retriever/real"
	"time"
)

type Retriever interface {
	Get(url string) string
}

type Poster interface {
	Post(url string, form map[string]string) string
}

const url = "http://www.imooc.com"

func download(r Retriever) string {
	return r.Get(url)
}

func post(poster Poster) {
	poster.Post(url, map[string]string{
		"name":   "ccmouse",
		"course": "golang",
	})
}

// 组合接口
type RetrieverPoster interface {
	Retriever
	Poster
}

func session(s RetrieverPoster) string {
	s.Post(url, map[string]string{
		"contents": "another faked imooc.com",
	})
	return s.Get(url)
}

func main() {
	var r Retriever
	retriever := mock.Retriever{"this is a fake imooc.com"}
	r = &retriever
	// r也可定义为mock.Retriever的地址，类型就会为*mock.Retriever
	//r = &mock.Retriever{"this is a fake imooc.com"}
	// 接口类型判断有两种方法：
	// 1、switch v := r.(type)
	inspect(r) // 接口肚子里存的是mock.Retriever的值（拷贝了一份）

	r = &real.Retriever{
		UserAgent: "Mozilla/5.0",
		TimeOut:   time.Minute,
	}
	inspect(r) // 接口肚子里存的是*real.Retriever指针，因为接口肚子里可以放指针，所以一般不用接口的指针

	// 2、Type assertion: r.(*real.Retriever)
	realRetriever := r.(*real.Retriever)
	fmt.Println(realRetriever.TimeOut)

	// 编译会报错，r的类型不匹配
	//mockRetriever := r.(*mock.Retriever)
	//fmt.Println(mockRetriever.Contents)
	// better
	if mockRetriever, ok := r.(*mock.Retriever); ok {
		fmt.Println(mockRetriever.Contents)
	} else {
		fmt.Println("not a mock retriever")
	}

	//fmt.Println(download(r))

	// #2 使用的是值接收者，不会改变这里的Contents
	//fmt.Println("Try a session")
	//fmt.Println(session(retriever))

	// #3
	fmt.Println("Try a session")
	fmt.Println(session(&retriever))
}

func inspect(r Retriever) {
	fmt.Println("Inspecting", r)
	fmt.Printf(" > %T %v\n", r, r)
	fmt.Print(" > Type switch:")
	switch v := r.(type) {
	// #1
	//case mock.Retriever:
	//	fmt.Println("Contents:", v.Contents)
	// #3
	case *mock.Retriever:
		fmt.Println("Contents:", v.Contents)
	case *real.Retriever:
		fmt.Println("UserAgent:", v.UserAgent)
	}
	fmt.Println()
}
